home *** CD-ROM | disk | FTP | other *** search
/ Aminet 7 / Aminet 7 - August 1995.iso / Aminet / comm / tcp / AmigaTCP.lha / AmigaTCP / src / ftpserv.c < prev    next >
C/C++ Source or Header  |  1989-06-24  |  12KB  |  520 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include "machdep.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "timer.h"
  10. #include "tcp.h"
  11. #include "ftp.h"
  12.  
  13. /* Command table */
  14. static char *commands[] = {
  15.     "user",
  16. #define    USER_CMD    0
  17.     "acct",
  18. #define    ACCT_CMD    1
  19.     "pass",
  20. #define    PASS_CMD    2
  21.     "type",
  22. #define    TYPE_CMD    3
  23.     "list",
  24. #define    LIST_CMD    4
  25.     "cwd",
  26. #define    CWD_CMD        5
  27.     "dele",
  28. #define    DELE_CMD    6
  29.     "name",
  30. #define    NAME_CMD    7
  31.     "quit",
  32. #define    QUIT_CMD    8
  33.     "retr",
  34. #define    RETR_CMD    9
  35.     "stor",
  36. #define    STOR_CMD    10
  37.     "port",
  38. #define    PORT_CMD    11
  39.     "nlst",
  40. #define    NLST_CMD    12
  41.     "pwd",
  42. #define    PWD_CMD        13
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44. #define    XPWD_CMD    14
  45.     NULLCHAR
  46. };
  47.  
  48. /* Response messages */
  49. static char banner[] = "220 %s FTP Ready\r\n";
  50. static char badcmd[] = "500 Unknown command\r\n";
  51. static char nopass[] = "202 Password not needed\r\n";
  52. static char logged[] = "230 Logged in\r\n";
  53. static char typeok[] = "200 Type OK\r\n";
  54. static char cwdok[] = "250 CWD OK\r\n";
  55. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  56. static char badtype[] = "501 Unknown type\r\n";
  57. static char badport[] = "501 Bad port syntax\r\n";
  58. static char unimp[] = "502 Command not yet implemented\r\n";
  59. static char bye[] = "221 Goodbye!\r\n";
  60. static char nodir[] = "553 Can't read directory\r\n";
  61. static char cantopen[] = "550 Can't open file\r\n";
  62. static char sending[] = "150 Opening data connection for %s %s\r\n";
  63. static char cantmake[] = "553 Can't create file\r\n";
  64. static char portok[] = "200 Port command okay\r\n";
  65. static char rxok[] = "226 File received OK\r\n";
  66. static char txok[] = "226 File sent OK\r\n";
  67.  
  68. static struct tcb *ftp_tcb;
  69.  
  70. /* Start up FTP service */
  71. ftp_start(argc,argv)
  72. int argc;
  73. char *argv[];
  74. {
  75.     struct socket lsocket;
  76.     void r_ftp(),s_ftp();
  77.  
  78.     lsocket.address = ip_addr;
  79.     if(argc < 2)
  80.         lsocket.port = FTP_PORT;
  81.     else
  82.         lsocket.port = atoi(argv[1]);
  83.  
  84.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_ftp,NULLVFP,s_ftp,0,(int *)NULL);
  85. }
  86. ftp_stop()
  87. {
  88.     if(ftp_tcb != NULLTCB)
  89.         close_tcp(ftp_tcb);
  90. }
  91. /* FTP server control channel connection state change upcall handler */
  92. static
  93. void
  94. s_ftp(tcb,old,new)
  95. struct tcb *tcb;
  96. char old,new;
  97. {
  98.     extern char hostname[];
  99.     struct ftp *ftp,*ftp_create();
  100.     void ftp_delete();
  101.     char *inet_ntoa(),*pwd();
  102.  
  103.     switch(new){
  104. #ifdef    QUICKSTART
  105.     case SYN_RECEIVED:
  106. #else
  107.     case ESTABLISHED:
  108. #endif
  109.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  110.             /* No space, kill connection */
  111.             close_tcp(tcb);
  112.             return;
  113.         }
  114.         ftp->control = tcb;    /* Downward link */
  115.         tcb->user = (int *)ftp;    /* Upward link */
  116.  
  117.         /* Set default data port */
  118.         ftp->port.address = tcb->conn.remote.address;
  119.         ftp->port.port = FTPD_PORT;
  120.  
  121.         /* Note current directory */
  122. #ifndef    AMIGA
  123.         ftp->cd = pwd();
  124. #endif
  125.         log(tcb,"open FTP");
  126.         tprintf(ftp->control,banner,hostname);
  127.         break;        
  128.     case CLOSE_WAIT:
  129.         close_tcp(tcb);
  130.         break;
  131.     case CLOSED:
  132.         log(tcb,"close FTP");
  133.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  134.             ftp_delete(ftp);
  135.         /* Check if server is being shut down */
  136.         if(tcb == ftp_tcb)
  137.             ftp_tcb = NULLTCB;
  138.         del_tcp(tcb);
  139.         break;
  140.     }
  141. }
  142.  
  143. /* FTP control channel receiver upcall handler */
  144. static
  145. void
  146. r_ftp(tcb,cnt)
  147. struct tcb *tcb;
  148. int16 cnt;
  149. {
  150.     register struct ftp *ftp;
  151.     char *index(),c;
  152.     struct mbuf *bp;
  153.     void docommand();
  154.  
  155.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  156.         /* Unknown connection, just kill it */
  157.         close_tcp(tcb);
  158.         return;
  159.     }
  160.     switch(ftp->state){
  161.     case COMMAND_STATE:
  162.         /* Assemble an input line in the session buffer. Return if incomplete */
  163.         recv_tcp(tcb,&bp,0);
  164.         while(pullup(&bp,&c,1) == 1){
  165.             switch(c){
  166.             case '\r':    /* Strip cr's */
  167.                 continue;
  168.             case '\n':    /* Complete line; process it */
  169.                 ftp->buf[ftp->cnt] = '\0';
  170.                 docommand(ftp);
  171.                 ftp->cnt = 0;
  172.                 break;
  173.             default:    /* Assemble line */
  174.                 if(ftp->cnt != LINELEN-1)
  175.                     ftp->buf[ftp->cnt++] = c;
  176.                 break;
  177.             }
  178.         }
  179.         /* else no linefeed present yet to terminate command */
  180.         break;
  181.     case SENDING_STATE:
  182.     case RECEIVING_STATE:
  183.         /* Leave commands pending on receive queue until
  184.          * present command is done
  185.          */
  186.         break;
  187.     }
  188. }
  189.  
  190. /* FTP server data channel connection state change upcall handler */
  191. void
  192. s_ftpd(tcb,old,new)
  193. struct tcb *tcb;
  194. char old,new;
  195. {
  196.     struct ftp *ftp;
  197. #ifndef    CPM
  198. #ifndef    AMIGA
  199.     char *cdsave;
  200. #endif
  201. #endif
  202.  
  203.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  204.         /* Unknown connection, kill it */
  205.         close_tcp(tcb);
  206.         return;
  207.     }
  208.     switch(new){
  209.     case FINWAIT2:
  210.     case TIME_WAIT:
  211.         if(ftp != NULLFTP && ftp->state == SENDING_STATE){
  212.             /* We've received an ack of our FIN, so
  213.              * send a completion message on the control channel
  214.              */
  215.             ftp->state = COMMAND_STATE;
  216.             tprintf(ftp->control,txok);
  217.             /* Kick command parser if something is waiting */
  218.             if(ftp->control->rcvcnt != 0)
  219.                 r_ftp(ftp->control,ftp->control->rcvcnt);
  220.         }
  221.         break;        
  222.     case CLOSE_WAIT:
  223.         close_tcp(tcb);
  224.         if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
  225.             /* End of file received on incoming file */
  226. #ifdef    CPM
  227.             if(ftp->type == ASCII_TYPE)
  228.                 putc(CTLZ,ftp->fp);
  229. #endif
  230. #ifndef    CPM
  231. #ifndef    AMIGA
  232.             cdsave = pwd();        /* Save current directory */
  233.             chdir(ftp->cd);        /* Switch to user's directory*/
  234. #endif
  235. #endif
  236.             fclose(ftp->fp);
  237. #ifndef    CPM
  238. #ifndef    AMIGA
  239.             if(cdsave != NULLCHAR){
  240.                 chdir(cdsave);        /* And back */
  241.                 free(cdsave);
  242.             }
  243. #endif
  244. #endif
  245.             ftp->fp = NULLFILE;
  246.             ftp->state = COMMAND_STATE;
  247.             tprintf(ftp->control,rxok);
  248.             /* Kick command parser if something is waiting */
  249.             if(ftp->control->rcvcnt != 0)
  250.                 r_ftp(ftp->control,ftp->control->rcvcnt);
  251.         }
  252.         break;
  253.     case CLOSED:
  254.         if(ftp != NULLFTP)
  255.             ftp->data = NULLTCB;
  256.         del_tcp(tcb);
  257.         break;
  258.     }
  259. }
  260.  
  261. /* Parse and execute ftp commands */
  262. static
  263. void
  264. docommand(ftp)
  265. register struct ftp *ftp;
  266. {
  267.     void r_ftpd(),t_ftpd(),s_ftpd();
  268.     char *cmd,*arg,*cp,**cmdp;
  269.     char *index(),*malloc(),*strcpy();
  270.     struct socket dport;
  271. #ifndef    CPM
  272. #ifndef    AMIGA
  273.     FILE *dir();
  274.     char *cdsave;
  275. #endif
  276. #endif
  277.  
  278.     cmd = ftp->buf;
  279.     if(ftp->cnt == 0){
  280.         /* Can't be a legal FTP command */
  281.         tprintf(ftp->control,badcmd);
  282.         return;
  283.     }    
  284.     cmd = ftp->buf;
  285.  
  286.     /* Translate entire buffer to lower case */
  287.     for(cp = cmd;*cp != '\0';cp++)
  288.         *cp = tolower(*cp);
  289.  
  290.     /* Find command in table; if not present, return syntax error */
  291.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  292.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  293.             break;
  294.     if(*cmdp == NULLCHAR){
  295.         tprintf(ftp->control,badcmd);
  296.         return;
  297.     }
  298.     arg = &cmd[strlen(*cmdp)];
  299.     while(*arg == ' ')
  300.         arg++;
  301.     /* Execute specific command */
  302.     switch(cmdp-commands){
  303.     case USER_CMD:
  304.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  305.             close_tcp(ftp->control);
  306.             break;
  307.         }
  308.         strcpy(ftp->username,arg);
  309.         tprintf(ftp->control,logged);
  310.         break;
  311.     case TYPE_CMD:
  312.         switch(*arg){
  313.         case 'a':    /* Ascii */
  314.             ftp->type = ASCII_TYPE;
  315.             tprintf(ftp->control,typeok);
  316.             break;
  317.         case 'b':    /* Binary */
  318.         case 'i':    /* Image */
  319.             ftp->type = IMAGE_TYPE;
  320.             tprintf(ftp->control,typeok);
  321.             break;
  322.         default:    /* Invalid */
  323.             tprintf(ftp->control,badtype);
  324.             break;
  325.         }
  326.         break;
  327.     case QUIT_CMD:
  328.         tprintf(ftp->control,bye);
  329.         close_tcp(ftp->control);
  330.         break;
  331.     case RETR_CMD:
  332.         /* Disk operation; return ACK now */
  333.         tcp_output(ftp->control);
  334. #ifndef    CPM
  335. #ifndef    AMIGA
  336.         cdsave = pwd();        /* Save current directory */
  337.         chdir(ftp->cd);        /* Switch to user's directory*/
  338. #endif
  339. #endif
  340.         ftp->fp = fopen(arg,"r");
  341. #ifndef    CPM
  342. #ifndef    AMIGA
  343.         chdir(cdsave);        /* And back */
  344.         free(cdsave);
  345. #endif
  346. #endif
  347.         if(ftp->fp == NULLFILE){
  348.             tprintf(ftp->control,cantopen);
  349.         } else {
  350.             log(ftp->control,"RETR %s/%s",ftp->cd,arg);
  351.             dport.address = ip_addr;
  352.             dport.port = FTPD_PORT;
  353.             ftp->state = SENDING_STATE;
  354.             tprintf(ftp->control,sending,"RETR",arg);
  355.  
  356.             /* This hack is just so we can talk to ourselves */
  357.             ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
  358.              0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  359.  
  360.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  361.              0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  362.         }
  363.         break;
  364.     case STOR_CMD:
  365.         /* Disk operation; return ACK now */
  366.         tcp_output(ftp->control);
  367. #ifndef    CPM
  368. #ifndef    AMIGA
  369.         cdsave = pwd();        /* Save current directory */
  370.         chdir(ftp->cd);        /* Switch to user's directory */
  371. #endif
  372. #endif    
  373.         ftp->fp = fopen(arg,"w");
  374. #ifndef    CPM
  375. #ifndef    AMIGA
  376.         chdir(cdsave);            /* And back */
  377.         free(cdsave);
  378. #endif
  379. #endif        
  380.         if(ftp->fp == NULLFILE){
  381.             tprintf(ftp->control,cantmake);
  382.         } else {
  383.             log(ftp->control,"STOR %s/%s",ftp->cd,arg);
  384.             dport.address = ip_addr;
  385.             dport.port = FTPD_PORT;
  386.             ftp->state = RECEIVING_STATE;
  387.             tprintf(ftp->control,sending,"STOR",arg);
  388.  
  389.             /* This hack is just so we can talk to ourselves */
  390.             ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
  391.              0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
  392.  
  393.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  394.              0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
  395.         }
  396.         break;
  397.     case PORT_CMD:
  398.         if(pport(&ftp->port,arg) == -1){
  399.             tprintf(ftp->control,badport);
  400.         } else {
  401.             tprintf(ftp->control,portok);
  402.         }
  403.         break;
  404. /* #ifndef CPM */
  405. #ifndef    AMIGA
  406.     case LIST_CMD:
  407.         /* Disk operation; return ACK now */
  408.         tcp_output(ftp->control);
  409.  
  410.         cdsave = pwd();        /* Save current directory */
  411.         chdir(ftp->cd);        /* Switch to user's directory */
  412.         ftp->fp = dir(arg,1);
  413.         chdir(cdsave);        /* And back */
  414.         free(cdsave);
  415.  
  416.         if(ftp->fp == NULLFILE){
  417.             tprintf(ftp->control,nodir);
  418.             break;
  419.         }            
  420.         dport.address = ip_addr;
  421.         dport.port = FTPD_PORT;
  422.         ftp->state = SENDING_STATE;
  423.         tprintf(ftp->control,sending,"LIST",arg);
  424.  
  425.         /* This hack is just so we can talk to ourselves */
  426.         ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
  427.          0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  428.  
  429.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  430.          0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  431.         break;
  432.     case NLST_CMD:
  433.         /* Disk operation; return ACK now */
  434.         tcp_output(ftp->control);
  435.  
  436.         cdsave = pwd();        /* Save current directory */
  437.         chdir(ftp->cd);        /* Switch to user's directory */
  438.         ftp->fp = dir(arg,0);
  439.         chdir(cdsave);        /* And back */
  440.         free(cdsave);
  441.  
  442.         if(ftp->fp == NULLFILE){
  443.             tprintf(ftp->control,nodir);
  444.             break;
  445.         }            
  446.         dport.address = ip_addr;
  447.         dport.port = FTPD_PORT;
  448.         ftp->state = SENDING_STATE;
  449.         tprintf(ftp->control,sending,"NLST",arg);
  450.  
  451.         /* This hack is just so we can talk to ourselves */
  452.         ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
  453.          0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  454.  
  455.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  456.          0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
  457.         break;
  458.     case CWD_CMD:
  459.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  460.  
  461.         cdsave = pwd();        /* Save current directory */
  462.         chdir(ftp->cd);        /* Go to user's context */
  463.         if(chdir(arg) == 0){    /* Attempt switch */
  464.             /* Succeeded, record in control block */
  465.             free(ftp->cd);
  466.             ftp->cd = pwd();
  467.             tprintf(ftp->control,cwdok);
  468.         } else {
  469.             /* Failed, don't change anything */
  470.             tprintf(ftp->control,nodir);
  471.         }
  472.         chdir(cdsave);            /* Go back */
  473.         free(cdsave);
  474.         break;
  475.     case XPWD_CMD:
  476.     case PWD_CMD:
  477.         tprintf(ftp->control,pwdmsg,ftp->cd);
  478.         break;
  479. #else
  480.     case LIST_CMD:
  481.     case NLST_CMD:
  482.     case CWD_CMD:
  483.     case XPWD_CMD:
  484.     case PWD_CMD:
  485. #endif
  486.     case ACCT_CMD:        
  487.     case DELE_CMD:
  488.         tprintf(ftp->control,unimp);
  489.         break;
  490.     case PASS_CMD:
  491.         tprintf(ftp->control,nopass);
  492.         break;
  493.     }
  494. }
  495. static
  496. int
  497. pport(sock,arg)
  498. struct socket *sock;
  499. char *arg;
  500. {
  501.     int32 n;
  502.     int atoi(),i;
  503.  
  504.     n = 0;
  505.     for(i=0;i<4;i++){
  506.         n = atoi(arg) + (n << 8);
  507.         if((arg = index(arg,',')) == NULLCHAR)
  508.             return -1;
  509.         arg++;
  510.     }
  511.     sock->address = n;
  512.     n = atoi(arg);
  513.     if((arg = index(arg,',')) == NULLCHAR)
  514.         return -1;
  515.     arg++;
  516.     n = atoi(arg) + (n << 8);
  517.     sock->port = n;
  518.     return 0;
  519. }
  520.